package com.sali.netty;

import com.sali.config.NettyConfig;
import com.sali.constants.CommonConstant;
import com.sali.entity.NettyBean;
import com.sali.netty.handler.ClientHandleInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.net.ConnectException;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: sali
 * @date: 2025/2/22 下午11:06.
 * @version: 1.0
 */
@Slf4j
public class NettyClient {

    private EventLoopGroup eventLoopGroup;

    private Bootstrap bootstrap = new Bootstrap();

    private NettyConfig nettyConfig;

    private Channel clientChannel;

    private boolean connectionFlag = false;

    public NettyClient(NettyConfig nettyConfig) {
        this.nettyConfig = nettyConfig;
        eventLoopGroup = new NioEventLoopGroup();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                //.option(ChannelOption.TCP_NODELAY, true)
                .handler(new ClientHandleInitializer(this));
    }

    /**
     * 异步连接
     */
    public boolean connect() {
        log.info("尝试连接到netty server:{}:{}", nettyConfig.getServerIp(), nettyConfig.getServerPort());
        try {
            ChannelFuture channelFuture = bootstrap.connect(nettyConfig.getServerIp(), nettyConfig.getServerPort()).sync();
            boolean notTimeOut = channelFuture.awaitUninterruptibly(30, TimeUnit.SECONDS);
            clientChannel = channelFuture.channel();
            if (notTimeOut) {
                if (clientChannel != null && clientChannel.isActive()) {
                    log.info("netty client started!!! {} connect to adapter", clientChannel.remoteAddress());
                    return true;
                }

                Throwable cause = channelFuture.cause();
                if (cause != null) {
                    exceptionHandle(cause);
                }
            } else {
                log.warn("connect remote host[{}] timeout {}s", clientChannel.remoteAddress(), 30);
            }
            log.info("netty client is running...");
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            exceptionHandle(e);
        }
        return false;
    }

    /**
     * 同步连接
     */
    public void connectAsync() {
        log.info("尝试连接到netty sever:{}:{}", nettyConfig.getServerIp(), nettyConfig.getServerPort());
        ChannelFuture channelFuture = bootstrap.connect(nettyConfig.getServerIp(), nettyConfig.getServerPort());
        channelFuture.addListener(future -> {
            Throwable cause = future.cause();
            if (cause != null) {
                connectionFlag = false;
                exceptionHandle(cause);
                log.info("等待下一次重连！");
                channelFuture.channel().eventLoop().schedule(this::connectAsync, 5, TimeUnit.SECONDS);
            } else {
                clientChannel = channelFuture.channel();
                if (clientChannel != null && clientChannel.isActive()) {
                    connectionFlag = true;
                    log.info("netty client started!!! {} connect to server", clientChannel.remoteAddress());
                }
            }
        });
    }

    /**
     * 异常处理
     */
    private void exceptionHandle(Throwable cause) {
        if (cause instanceof ConnectException) {
            log.error("连接异常：{}", cause.getMessage());
        } else if (cause instanceof ClosedChannelException) {
            log.error("connect error: {}", "client has destroy");
        } else {
            log.error("connect error.", cause);
        }
    }

    /**
     * 关闭连接
     */
    public void close() {
        if (eventLoopGroup != null) {
            eventLoopGroup.shutdownGracefully();
        }
        this.connectionFlag = false;
    }

    /**
     * 启动连接
     */
    public void start(ScheduledExecutorService scheduledExecutorService) {
        while (true) {
            boolean connect = this.connect();
            if (connect) {
                connectionFlag = connect;
                break;
            }

            try {
                log.error("无法连接到服务端5s后重试!!");
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                log.error("客户端连接线程中断!! messsage is {}", e.getMessage(), e);
                Thread.currentThread().interrupt();
            }
        }

        // 定时发送心跳
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            if (this.getClientChannel().isActive()) {
                this.sendData(new NettyBean(CommonConstant.HEARTBEAT_FLAG, CommonConstant.HEARTBEAT_CONTENT));
            }
        }, 0, 5, TimeUnit.SECONDS);
    }

    /**
     * 获取channel
     */
    public Channel getClientChannel() {
        return clientChannel;
    }

    public void sendData(NettyBean protocolBean) {
        ByteBuf buffer = Unpooled.buffer(protocolBean.getLength() + 5);
        buffer.writeByte(protocolBean.getFlag());
        buffer.writeInt(protocolBean.getLength());
        buffer.writeBytes(protocolBean.getDatas());
        this.getClientChannel().writeAndFlush(buffer);
    }

    public boolean isConnectionFlag() {
        return connectionFlag;
    }

}
